package com.harmony.pilot.knowledge.service;

import com.harmony.pilot.knowledge.entity.CodeSnippet;
import com.harmony.pilot.knowledge.entity.FileMetadata;
import com.harmony.pilot.knowledge.parser.CodeParser;
import com.harmony.pilot.knowledge.repository.CodeSnippetRepository;
import com.harmony.pilot.knowledge.repository.FileMetadataRepository;
import com.harmony.pilot.knowledge.scanner.CodeScanner;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 扫描服务
 * 管理代码扫描和索引任务
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ScanService {
    
    private final CodeScanner codeScanner;
    private final CodeParser codeParser;
    private final FileMetadataRepository fileMetadataRepository;
    private final CodeSnippetRepository snippetRepository;
    private final EmbeddingService embeddingService;
    private final GitService gitService;
    
    // 综合分析服务
    private final BundleJsonService bundleJsonService;
    private final GniFileService gniFileService;
    private final GnSourceMappingService gnSourceMappingService;
    private final HeaderDependencyAnalysisService headerDependencyAnalysisService;
    
    @Value("${knowledge.scan.thread-pool-size:16}")
    private int threadPoolSize;
    
    @Value("${knowledge.scan.batch-size:1000}")
    private int batchSize;
    
    @Value("${knowledge.scan.incremental.enabled:true}")
    private boolean incrementalEnabled;
    
    @Value("${knowledge.scan.incremental.use-git:true}")
    private boolean useGitForIncremental;
    
    private volatile boolean isScanning = false;
    private volatile ScanStatus currentStatus;
    
    // 线程池用于并行扫描
    private ExecutorService executorService;
    
    /**
     * 开始全量扫描（并行优化版）
     */
    @Async
    public CompletableFuture<ScanResult> startFullScan() {
        if (isScanning) {
            log.warn("扫描任务已在运行中");
            return CompletableFuture.completedFuture(
                new ScanResult(false, "扫描任务已在运行中", null)
            );
        }
        
        isScanning = true;
        currentStatus = new ScanStatus();
        
        // 初始化线程池
        executorService = Executors.newFixedThreadPool(threadPoolSize);
        
        try {
            log.info("开始全量扫描OpenHarmony代码库（并行度: {}）", threadPoolSize);
            
            // 1. 扫描文件系统
            List<FileMetadata> files = codeScanner.scanAllFiles();
            currentStatus.setTotalFiles(files.size());
            log.info("发现 {} 个源文件", files.size());
            
            // 2. 批量保存文件元数据
            log.info("批量保存文件元数据（批次大小: {}）...", batchSize);
            saveInBatches(files, fileMetadataRepository);
            
            // 3. 并行解析代码并生成片段
            log.info("开始并行解析代码（线程数: {}）...", threadPoolSize);
            AtomicInteger processedCount = new AtomicInteger(0);
            AtomicInteger successCount = new AtomicInteger(0);
            AtomicInteger failCount = new AtomicInteger(0);
            
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            BlockingQueue<FileMetadata> fileQueue = new LinkedBlockingQueue<>(files);
            
            // 提交并行处理任务
            for (int i = 0; i < threadPoolSize; i++) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    while (!fileQueue.isEmpty()) {
                        FileMetadata file = fileQueue.poll();
                        if (file == null) {
                            break;
                        }
                        
                        try {
                            processFile(file);
                            successCount.incrementAndGet();
                        } catch (Exception e) {
                            log.error("处理文件失败: {}", file.getFilePath(), e);
                            file.setScanStatus("FAILED");
                            file.setScanError(e.getMessage());
                            failCount.incrementAndGet();
                        } finally {
                            int processed = processedCount.incrementAndGet();
                            if (processed % 100 == 0) {
                                double progress = 100.0 * processed / files.size();
                                log.info("进度: {}/{} ({:.1f}%)", 
                                    processed, files.size(), progress);
                                
                                currentStatus.setProcessedFiles(processed);
                                currentStatus.setSuccessCount(successCount.get());
                                currentStatus.setFailCount(failCount.get());
                            }
                        }
                    }
                }, executorService);
                futures.add(future);
            }
            
            // 等待所有任务完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            
            // 4. 批量保存所有更新
            log.info("批量保存文件状态更新...");
            saveInBatches(files, fileMetadataRepository);
            
            // 5. 后处理：建立GN源文件映射关系
            log.info("建立GN源文件映射关系...");
            try {
                gnSourceMappingService.buildAllMappings();
            } catch (Exception e) {
                log.error("建立GN映射关系失败", e);
            }
            
            // 6. 后处理：批量分析头文件依赖关系
            log.info("批量分析头文件依赖关系...");
            try {
                List<FileMetadata> cppFiles = files.stream()
                    .filter(f -> {
                        String type = f.getFileType();
                        return ".h".equals(type) || ".hpp".equals(type) || 
                               ".cpp".equals(type) || ".cc".equals(type) || ".c".equals(type);
                    })
                    .collect(java.util.stream.Collectors.toList());
                headerDependencyAnalysisService.analyzeAndSaveDependencies(cppFiles);
            } catch (Exception e) {
                log.error("分析头文件依赖失败", e);
            }
            
            log.info("扫描完成！成功: {}, 失败: {}", successCount.get(), failCount.get());
            
            ScanResult result = new ScanResult(
                true,
                String.format("扫描完成！总共: %d, 成功: %d, 失败: %d", 
                    files.size(), successCount.get(), failCount.get()),
                currentStatus
            );
            
            return CompletableFuture.completedFuture(result);
            
        } catch (IOException e) {
            log.error("扫描失败", e);
            return CompletableFuture.completedFuture(
                new ScanResult(false, "扫描失败: " + e.getMessage(), currentStatus)
            );
        } finally {
            isScanning = false;
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }
    
    /**
     * 批量保存
     */
    private <T> void saveInBatches(List<T> items, org.springframework.data.repository.CrudRepository<T, ?> repository) {
        for (int i = 0; i < items.size(); i += batchSize) {
            int end = Math.min(i + batchSize, items.size());
            List<T> batch = items.subList(i, end);
            repository.saveAll(batch);
            
            if (i % (batchSize * 10) == 0) {
                log.debug("已保存 {}/{} 条记录", end, items.size());
            }
        }
    }
    
    /**
     * 处理单个文件（集成所有分析服务）
     */
    @Transactional
    protected void processFile(FileMetadata file) throws IOException {
        file.setScanStatus("PROCESSING");
        
        String fileName = file.getFileName();
        String filePath = file.getFilePath();
        String fileType = file.getFileType();
        
        // 读取文件内容
        String content = codeScanner.readFileContent(filePath);
        
        try {
            // 1. 处理bundle.json文件
            if ("bundle.json".equals(fileName)) {
                log.debug("处理bundle.json文件: {}", filePath);
                var bundle = bundleJsonService.parseBundleJson(filePath);
                if (bundle != null) {
                    bundleJsonService.saveBundleJson(bundle);
                }
            }
            // 2. 处理.gni文件
            else if (".gni".equals(fileType)) {
                log.debug("处理.gni文件: {}", filePath);
                var gniFile = gniFileService.parseGniFile(filePath);
                if (gniFile != null) {
                    gniFileService.saveGniFile(gniFile);
                }
            }
            // 3. 处理BUILD.gn文件 - 建立源文件映射
            else if ("BUILD.gn".equals(fileName) || ".gn".equals(fileType)) {
                log.debug("处理BUILD.gn文件: {}", filePath);
                // 注意：GN文件的target解析需要在GN解析器中完成
                // 这里先保存文件元数据，后续在批量处理阶段建立映射
            }
            
            // 4. 处理C++源文件（.h/.cpp）- 分析头文件依赖（在批量处理阶段统一保存）
            // 注意：头文件依赖分析在扫描完成后批量处理以提高性能
            
            // 5. 解析代码片段（所有文件类型）
            List<CodeSnippet> snippets = codeParser.parseFile(file, content);
            snippetRepository.saveAll(snippets);
            
            // 更新文件状态
            file.setScanStatus("COMPLETED");
            file.setSnippetCount(snippets.size());
            
            log.debug("文件处理完成: {}, 生成 {} 个代码片段", fileName, snippets.size());
            
        } catch (Exception e) {
            log.error("处理文件失败: {}", filePath, e);
            file.setScanStatus("FAILED");
            throw e;
        }
    }
    
    /**
     * 增量扫描（只处理变更的文件）
     */
    @Async
    public CompletableFuture<ScanResult> startIncrementalScan(String lastCommitHash) {
        if (isScanning) {
            log.warn("扫描任务已在运行中");
            return CompletableFuture.completedFuture(
                new ScanResult(false, "扫描任务已在运行中", null)
            );
        }
        
        if (!incrementalEnabled) {
            log.warn("增量扫描未启用");
            return CompletableFuture.completedFuture(
                new ScanResult(false, "增量扫描未启用", null)
            );
        }
        
        isScanning = true;
        currentStatus = new ScanStatus();
        executorService = Executors.newFixedThreadPool(threadPoolSize);
        
        try {
            log.info("开始增量扫描（基于Git变更）");
            
            List<String> changedFiles;
            
            if (useGitForIncremental) {
                // 1. 从Git获取变更文件列表
                changedFiles = gitService.getChangedFiles(lastCommitHash);
                log.info("从Git获取到 {} 个变更文件", changedFiles.size());
            } else {
                // 2. 基于MD5比较的增量扫描
                changedFiles = findChangedFilesByMd5();
                log.info("基于MD5比较发现 {} 个变更文件", changedFiles.size());
            }
            
            if (changedFiles.isEmpty()) {
                log.info("没有变更文件需要处理");
                return CompletableFuture.completedFuture(
                    new ScanResult(true, "没有变更文件", currentStatus)
                );
            }
            
            currentStatus.setTotalFiles(changedFiles.size());
            
            // 3. 转换文件路径并创建FileMetadata
            List<FileMetadata> filesToProcess = new ArrayList<>();
            for (String relativePath : changedFiles) {
                try {
                    String absolutePath = gitService.toAbsolutePath(relativePath);
                    if (Files.exists(Paths.get(absolutePath))) {
                        FileMetadata metadata = codeScanner.createFileMetadata(
                            Paths.get(absolutePath), 
                            Files.readAttributes(Paths.get(absolutePath), 
                                java.nio.file.attribute.BasicFileAttributes.class)
                        );
                        filesToProcess.add(metadata);
                    }
                } catch (Exception e) {
                    log.warn("处理文件路径失败: {}", relativePath, e);
                }
            }
            
            log.info("准备处理 {} 个变更文件", filesToProcess.size());
            
            // 4. 并行处理变更文件
            AtomicInteger processedCount = new AtomicInteger(0);
            AtomicInteger successCount = new AtomicInteger(0);
            AtomicInteger failCount = new AtomicInteger(0);
            
            List<CompletableFuture<Void>> futures = new ArrayList<>();
            BlockingQueue<FileMetadata> fileQueue = new LinkedBlockingQueue<>(filesToProcess);
            
            for (int i = 0; i < threadPoolSize; i++) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
                    while (!fileQueue.isEmpty()) {
                        FileMetadata file = fileQueue.poll();
                        if (file == null) {
                            break;
                        }
                        
                        try {
                            // 检查是否需要更新（比较MD5）
                            Optional<FileMetadata> existing = fileMetadataRepository
                                .findByFilePath(file.getFilePath());
                            
                            if (existing.isPresent()) {
                                FileMetadata existingFile = existing.get();
                                if (existingFile.getFileMd5().equals(file.getFileMd5())) {
                                    log.debug("文件未变更，跳过: {}", file.getFilePath());
                                    processedCount.incrementAndGet();
                                    continue;
                                }
                            }
                            
                            processFile(file);
                            successCount.incrementAndGet();
                        } catch (Exception e) {
                            log.error("处理文件失败: {}", file.getFilePath(), e);
                            file.setScanStatus("FAILED");
                            file.setScanError(e.getMessage());
                            failCount.incrementAndGet();
                        } finally {
                            int processed = processedCount.incrementAndGet();
                            if (processed % 100 == 0) {
                                currentStatus.setProcessedFiles(processed);
                                currentStatus.setSuccessCount(successCount.get());
                                currentStatus.setFailCount(failCount.get());
                            }
                        }
                    }
                }, executorService);
                futures.add(future);
            }
            
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
            
            // 5. 保存更新
            saveInBatches(filesToProcess, fileMetadataRepository);
            
            // 6. 更新最后一次扫描的commit hash
            String currentCommitHash = gitService.getCurrentCommitHash();
            log.info("当前commit hash: {}", currentCommitHash);
            
            log.info("增量扫描完成！成功: {}, 失败: {}", successCount.get(), failCount.get());
            
            ScanResult result = new ScanResult(
                true,
                String.format("增量扫描完成！处理: %d, 成功: %d, 失败: %d", 
                    filesToProcess.size(), successCount.get(), failCount.get()),
                currentStatus
            );
            
            return CompletableFuture.completedFuture(result);
            
        } catch (Exception e) {
            log.error("增量扫描失败", e);
            return CompletableFuture.completedFuture(
                new ScanResult(false, "增量扫描失败: " + e.getMessage(), currentStatus)
            );
        } finally {
            isScanning = false;
            if (executorService != null) {
                executorService.shutdown();
            }
        }
    }
    
    /**
     * 基于MD5比较找出变更文件
     */
    private List<String> findChangedFilesByMd5() throws IOException {
        List<String> changedFiles = new ArrayList<>();
        
        // 获取所有文件
        List<FileMetadata> allFiles = codeScanner.scanAllFiles();
        
        for (FileMetadata file : allFiles) {
            Optional<FileMetadata> existing = fileMetadataRepository
                .findByFilePath(file.getFilePath());
            
            if (existing.isEmpty()) {
                // 新文件
                changedFiles.add(file.getRelativePath());
            } else {
                // 检查MD5是否变化
                FileMetadata existingFile = existing.get();
                if (!existingFile.getFileMd5().equals(file.getFileMd5())) {
                    changedFiles.add(file.getRelativePath());
                }
            }
        }
        
        return changedFiles;
    }
    
    /**
     * 获取扫描状态
     */
    public ScanStatus getScanStatus() {
        return currentStatus != null ? currentStatus : new ScanStatus();
    }
    
    /**
     * 是否正在扫描
     */
    public boolean isScanning() {
        return isScanning;
    }
    
    // DTO类
    public static class ScanResult {
        private boolean success;
        private String message;
        private ScanStatus status;
        
        public ScanResult(boolean success, String message, ScanStatus status) {
            this.success = success;
            this.message = message;
            this.status = status;
        }
        
        // getters
        public boolean isSuccess() { return success; }
        public String getMessage() { return message; }
        public ScanStatus getStatus() { return status; }
    }
    
    public static class ScanStatus {
        private int totalFiles;
        private int processedFiles;
        private int successCount;
        private int failCount;
        private long startTime = System.currentTimeMillis();
        
        // getters and setters
        public int getTotalFiles() { return totalFiles; }
        public void setTotalFiles(int totalFiles) { this.totalFiles = totalFiles; }
        public int getProcessedFiles() { return processedFiles; }
        public void setProcessedFiles(int processedFiles) { this.processedFiles = processedFiles; }
        public int getSuccessCount() { return successCount; }
        public void setSuccessCount(int successCount) { this.successCount = successCount; }
        public int getFailCount() { return failCount; }
        public void setFailCount(int failCount) { this.failCount = failCount; }
        public long getStartTime() { return startTime; }
        
        public double getProgress() {
            return totalFiles > 0 ? 100.0 * processedFiles / totalFiles : 0.0;
        }
        
        public long getElapsedTime() {
            return System.currentTimeMillis() - startTime;
        }
    }
}

